import { NodeTreeFormatMeta, NodeTreeFormatData, Direction, NodeArrayFormatMeta, NodeArrayFormatData } from './models'
import { Mind } from './Mind'
import { Node } from './Node'

export class NodeTreeFormat {
    static example: NodeTreeFormatMeta = {
        meta: {
            name: 'mango',
            author: 'limaoquan',
            version: 'v0.1'
        },
        format: 'node_tree',
        data: {
            id: 'root',
            topic: 'jsMind Example'
        }
    }

    /**
     * 根据传入的数据（与 example 类似，可能是用户传入的）
     * @param source 
     */
    getMind (source: NodeTreeFormatMeta) {
        let mind = new Mind()
        // 将 source 中的 name author version 设置为 mind 的属性
        mind.name = source.meta.name
        mind.author = source.meta.author
        mind.version = source.meta.version
        
        NodeTreeFormat._parse(mind, source.data)

        return mind
    }

    /**
     * 根据 Mind 对象，获取数据，相当于将 mind 中的Node 数据，转换为 json 格式
     * @param mind 
     */
    getData (mind) {
        var json: NodeTreeFormatMeta = {};
        json.meta = {
            name : mind.name,
            author : mind.author,
            version : mind.version
        };
        json.format = 'node_tree';
        json.data = NodeTreeFormat._buildnode(mind.root);
        return json;
    }

    /**
     * 将 data 数据 解析至 mind
     * @param mind 
     * @param data 
     */
    private static _parse(mind: Mind, data: NodeTreeFormatData) {
        
        var _data = NodeTreeFormat._extract_data(data);

        // 设置根节点
        mind.setRoot(data.id, data.topic, data);

        if('children' in data){
            var children = data.children;
            for(var i = 0; i < children.length; i++){
                NodeTreeFormat._extract_subnode(mind, mind.root, children[i]);
            }
        }
    }

    /**
     * 过滤出 data 根节点 中除 id, topic, children, expanded 属性外的属性
     * @param data 
     */
    private static _extract_data(data: NodeTreeFormatData) {
        var _data = {};
        for(var k in data){
            if(k == 'id' || k=='topic' || k=='children' || k=='direction' || k=='expanded'){
                continue;
            }
            _data[k] = data[k];
        }
        return _data;
    }

    /**
     * 将子节点放入 parent 节点 中
     * @param mind 
     * @param parent 
     * @param child 
     */
    private static _extract_subnode(mind: Mind, parent: Node, child: NodeTreeFormatData) {
        var data = NodeTreeFormat._extract_data(child);
        var d = null;
        if(parent.isroot){
            d = child.direction == 'left' ? Direction.Left : Direction.Right;
        }
        var node = mind.addNode(parent, child.id, child.topic, data, null, d, node.expanded);
        if('children' in child){
            var children = child.children;
            for(var i = 0; i < children.length; i++){
                NodeTreeFormat._extract_subnode(mind, node, children[i]);
            }
        }
    }

    /**
     * 将 node 及其子节点的数据 转换为 json 格式
     * @param node 
     */
    private static _buildnode (node: Node) {
        if(!(node instanceof Node)){return;}

        var o: NodeTreeFormatData = {
            id : node.id,
            topic : node.topic,
            expanded : node.expanded
        };

        // 如果父节点是根节点，方向
        if(!!node.parent && node.parent.isroot){
            o.direction = node.direction == Direction.Left ? 'left' : 'right';
        }

        // 将 data 中的数据复制至 json 中
        if(node.data != null){
            var node_data = node.data;
            for(var k in node_data){
                o[k] = node_data[k];
            }
        }
        var children = node.children;
        if(children.length > 0){
            o.children = [];
            for(var i=0;i<children.length;i++){
                o.children.push(NodeTreeFormat._buildnode(children[i]));
            }
        }
        return o
    }
}


export class NodeArrayFormat {
    static example: NodeArrayFormatMeta = {
        meta: {
            name: 'mango',
            author: 'limaoquan',
            version: 'v0.1'
        },
        format: 'node_array',
        data: [
            {
                id: 'root',
                topic: 'jsMind Example',
                isroot: true
            }
        ]
    }
    /**
     * 根据传入的数据（与 example 类似，可能是用户传入的） 获得 Mind 对象
     * @param source 
     */
    getMind (source: NodeArrayFormatMeta) {
        var mind = new Mind();
        mind.name = source.meta.name;
        mind.author = source.meta.author;
        mind.version = source.meta.version;
        NodeArrayFormat._parse(mind, source.data);
        return mind;
    }
    
    /**
     * 根据 Mind 对象，获取数据，相当于将 mind 中的Node 数据，转换为 json 格式
     * @param mind 
     */
    getData (mind: Mind) {
        var json: NodeArrayFormatMeta = {};
        json.meta = {
            name : mind.name,
            author : mind.author,
            version : mind.version
        };
        json.format = 'node_array';
        json.data = [];
        NodeArrayFormat._array(mind,json.data);
        return json;
    }


    /**
     * 将 data 数据 解析至 mind 
     * @param mind 
     * @param data 
     */
    private static _parse(mind: Mind, data: NodeArrayFormatData[]) {
        var narray = data.slice(0);
        // reverse array for improving looping performance
        narray.reverse();
        var root_id = NodeArrayFormat._extract_root(mind, narray);
        if(!!root_id){
            NodeArrayFormat._extract_subnode(mind, root_id, narray);
        }else{
            console.error('root node can not be found');
        }
    }

    private static _extract_root(mind: Mind, dataArr: NodeArrayFormatData[]) {
        var i = dataArr.length;
        while(i--){
            if('isroot' in dataArr[i] && dataArr[i].isroot){
                var root_json = dataArr[i];
                var data = NodeArrayFormat._extract_data(root_json);
                mind.setRoot(root_json.id,root_json.topic,data);
                dataArr.splice(i,1);
                return root_json.id;
            }
        }
        return null;
    }

    private static _extract_data(data: NodeArrayFormatData) {
        var _data = {};
        for(var k in data){
            if(k == 'id' || k=='topic' || k=='parentid' || k=='isroot' || k=='direction' || k=='expanded'){
                continue;
            }
            _data[k] = data[k];
        }
        return _data;
    }

    private static _extract_subnode (mind: Mind, parentid: string, dataArr: NodeArrayFormatData[]) {
        var i = dataArr.length;
        var node_json = null;
        var data = null;
        var extract_count = 0;
        while(i--){
            node_json = dataArr[i];
            if(node_json.parentid == parentid){
                data = NodeArrayFormat._extract_data(node_json);
                var d = null;
                var node_direction = node_json.direction;
                if(!!node_direction){
                    d = node_direction == 'left' ? Direction.Left : Direction.Right;
                }
                mind.addNode(parentid, node_json.id, node_json.topic, data, null, d, node_json.expanded);
                dataArr.splice(i,1);
                extract_count ++;
                var sub_extract_count = NodeArrayFormat._extract_subnode(mind, node_json.id, dataArr);
                if(sub_extract_count > 0){
                    // reset loop index after extract subordinate node
                    i = dataArr.length;
                    extract_count += sub_extract_count;
                }
            }
        }
        return extract_count;
    }

    private static _array (mind: Mind, dataArr: NodeArrayFormatData[]) {
        NodeArrayFormat._array_node(mind.root, dataArr)
    }

    private static _array_node (node: Node, dataArr: NodeArrayFormatData[]) {
        if(!(node instanceof Node)){return;}
        var o: NodeArrayFormatData = {
            id : node.id,
            topic : node.topic,
            expanded : node.expanded
        };
        if(!!node.parent){
            o.parentid = node.parent.id;
        }
        if(node.isroot){
            o.isroot = true;
        }
        if(!!node.parent && node.parent.isroot){
            o.direction = node.direction == Direction.Left ? 'left' : 'right';
        }
        if(node.data != null){
            var node_data = node.data;
            for(var k in node_data){
                o[k] = node_data[k];
            }
        }
        dataArr.push(o);
        var ci = node.children.length;
        for(var i=0;i<ci;i++){
            NodeArrayFormat._array_node(node.children[i], dataArr);
        }
    }
}